﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using Layers;
using System.Text;
using System.ComponentModel;

namespace HoldemGame
{
    using HoldemHand;

    public class GameList<T> : Layers.ObservableListWrapper<T>, INotifyCollectionChanged where T:GameObject
    {
        public Game Game;

        public T Find(string id)
        {
            foreach (T p in _impl)
            {
                if (p.ID.Equals(id))
                    return p;
            }
            return null;
        }

        public T Seek(T curr, int dir, Predicate<T> filter )
        {
            int i = _impl.IndexOf(curr);
            if (i < 0)
                return null;
            int step = 0;
            while (step++ < Count)
            {
                i += dir;
                if (i >= Count)
                    i -= Count;
                if (i < 0)
                    i += Count;
                T next = _impl[i];
                if (null != next)
                    if (filter(next))
                        return next;
            }
            return null;
        }

     

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (T p in this)
            {
                if (i > 0) sb.Append(",");
                sb.AppendFormat("{0}: {1}", i, p);
            }
            return sb.ToString();
        }

        public void AddAfter(T p, T after)
        {
            int index = _impl.IndexOf(after);
            if (index >= 0)
                base.Insert(index+1, p);
            else
                base.Add(p);
            p.Game = Game;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, p, index));
        }

        public void AddBefore(T p, T before)
        {
            int index = _impl.IndexOf(before);
            if (index >= 0)
                base.Insert(index, p);
            else
                base.Insert(0,p);
            p.Game = Game;
            //OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, p, index));
        }


        public void Add(T p)
        {
            base.Add(p);
            p.Game = Game;
            //OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, p, _impl.IndexOf(p)));
        }

        public void Remove(T p)
        {
            int index = _impl.IndexOf(p);
            base.Remove(p);
            //OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, p, index));
        }

   
    }

    public class GamePlayers : GameList<GamePlayer>
    {
        private GamePlayer _first;
        private GamePlayer _current;

        public int PositionOf(GamePlayer player)
        {
            int pos = _impl.IndexOf(player)-_impl.IndexOf(_first);
            if (pos < 0)
                pos += Count;
            return pos;
        }

        public GamePlayer Seek(GamePlayer curr, int dir, bool activeOnly)
        {
            if (activeOnly && ActiveCount == 0)
                throw new InvalidOperationException();

            int i = _impl.IndexOf(curr);
            if (i < 0)
                return null;
            int step = 0;
            while (step++<Count)
            {
                i += dir;
                if (i >= Count)
                    i -= Count;
                if (i < 0)
                    i += Count;
                GamePlayer next = _impl[i];
                if (null != next)
                    if (!activeOnly)
                        return next;
                    else if (next.IsActive)
                        return next;
            }
            return null;
        }

        public void Listen(GameSeats seats)
        {
            seats.SeatEvent += new EventHandler<SeatEventArgs>(seats_SeatEvent);
        }

        void seats_SeatEvent(object sender, SeatEventArgs e)
        {
            if(e.Kind==SeatEventArgs.EventKind.SitDown)
            {
                GameSeat prevSeat = e.Seat.Seats.Seek(e.Seat, -1, delegate(GameSeat s) { return !s.IsFree; });
                AddAfter(prevSeat==null?null:prevSeat.Player, e.Player);
            }else if(e.Kind==SeatEventArgs.EventKind.StandUp)
            {
                Remove(e.Seat.Player);
            }
            else if (e.Kind == SeatEventArgs.EventKind.TableResize)
            {
                Clear();
            }
        }

        public void PlaceBefore(GamePlayer player, GamePlayer newPlayer)
        {
            int idxNew = IndexOf(newPlayer);
            int idxOld = IndexOf(player);
            if (idxNew >= 0)
                Remove(newPlayer);
            AddBefore(newPlayer, player);

            this.Game.StateChanged();
        }

        public void Replace(GamePlayer pold, GamePlayer pnew)
        {
            int idx = IndexOf(pold);
            Remove(pold);
            _impl.Insert(idx, pnew);

            this.Game.StateChanged();
        }

        
        public void Swap(GamePlayer pone, GamePlayer ptwo)
        {
            int a, b;
            if ((a=IndexOf(pone)) < 0)
                throw new InvalidOperationException();
            if ((b=IndexOf(ptwo)) < 0)
                throw new InvalidOperationException();
            _impl[a] = ptwo;
            _impl[b] = pone;
            this.Game.StateChanged();
        }

        public GamePlayer First 
        { 
            get 
            { 
                return _first; 
            } 
            set 
            {
                _first = value;

            } 
        }

        public void SwitchNext()
        {
            if (ActiveCount == 0)
                throw new InvalidOperationException();
            _current = this.Seek(_current, 1, true);
            if (!_current.IsActive)
                throw new InvalidOperationException();
        }

        public GamePlayer FromName(string playerName)
        {
            int i = 0;
            for (i=0;i<_impl.Count;i++)
            {
                GamePlayer p = _impl[i];
                if (p.ID.Equals(playerName))
                    return p;
            }
            return null;
        }

        public GamePlayer Current
        {
            get
            {
                return _current;
            }
            set
            {
                _current = value;
           }
        }

        public GamePlayers(Game game):base()
        {
            Game = game;
            base.CollectionChanged += new NotifyCollectionChangedEventHandler(GamePlayers_CollectionChanged);
        }

        void GamePlayers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                if (null == _first)
                    _first = this[0];
                if (null == _current)
                    _current = _first;
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                if (e.OldItems.Contains(_first))
                    if (Count > 0)
                        _first = this[0];
                    else
                        _first = null;

                if (e.OldItems.Contains(_current))
                    _current = _first;
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
                if (Count > 0)
                    _first = _current = this[0];
        }

 

        public IEnumerable<GamePlayer> Active
        {
            get { return FilterByState(0, PlayerState.Folded); }
        }

        public IEnumerable<GamePlayer> Shown
        {
            get { return FilterByState(PlayerState.Shown, PlayerState.Shown); }
        }

        public IEnumerable<GamePlayer> Hidden
        {
            get { return FilterByState(0, PlayerState.Shown); }
        }


        

        public int ActiveCount 
        { 
            get 
            {
                int count = 0;
                foreach (GamePlayer p in this)
                {
                    if (p.IsActive)
                        count++;
                }
                return count;
            } 
        }

        public IEnumerable<GamePlayer> FilterByState(int flags, int mask)
        {
            foreach (GamePlayer p in this)
            {
                if ((p.State.ToInt() & mask) == flags)
                    yield return p;
            }
        }
 
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (GamePlayer p in this)
            {
                if (i > 0) sb.Append(",");
                sb.AppendFormat("{0}: {1}", i, p);
                if (p == Current)
                    sb.Append(" = curr");
            }
            return sb.ToString();
        }
        
    }

    public static class PlayerState
    {
        public static int Folded = 0x01;
        public static int Shown = 0x02;
        public static int Winner = 0x04;
        public static int ForceShow = 0x08;
        public static int ManualMode = 0x10;
    }


    public class PlayerInfo
    {
        public ICardSetList EstCards{get;set;}  // estimated cards
        public CardValue EstCardValue{get;set;} // estimated cards value
        public GamePlayer Player{get;set;}
        public Odds Odds{get;set;}

        public class CompareByValue : IComparer<PlayerInfo>
        {
            #region IComparer<PlayerInfo> Members

            public int Compare(PlayerInfo x, PlayerInfo y)
            {
                return x.EstCardValue.CompareTo(y.EstCardValue);
            }

            #endregion
        }
        public PlayerInfo(GamePlayer p, ICardSetList cards, CardValue value)
        {
            Player = p;
            EstCards = cards;
            EstCardValue = value;
            Odds = new Odds();
        }

        public PlayerInfo(GamePlayer p):this(p, CardSetList.Random, new CardValue(0.5))
        {

        }

        public PlayerInfo():this(null){}
    }

    /// <summary>
    /// Represents player state
    /// Contains player current state flags (Folded, Active, SmallBlind, BigBlind, Button)
    /// Contains player position (0=SB, 1=BB,...,6=Button)
    /// </summary>

    public class GamePlayer : GameObject, IContainer
    {
        private string _id = "(empty)";
        private BitSet _state = new BitSet(0);
        private double _stake = 0.0;
        private double _bank = 0.0;
        private IBrains _brains;
        private IList<HoldemAction> _hist;
        private int _raiseCount;
        private int _callCount;
        private uint _handValue;
        private BrainAction _brainAction;
        private PlayerInfo _info;
        private BrainAction _lastAction;
        private GameSeat _seat;
        private CardSet _pocketCards;

        public GamePlayer()
        {
            _brains = new CallBrains();
            _hist = null;
            _info = new PlayerInfo(this);
            _pocketCards = new CardSet(2);
        }

        public GamePlayer(Game g,String id)
            : this()
        {
            Game = g;
            _id = id;
        }

        public GameSeat Seat { get { return _seat; } set { _seat = value; } }

        public PlayerInfo Info { get { return _info; } set { _info = value;} }

        public BrainAction BrainAction { get { return _brainAction; } set { _brainAction=value;} }

        public BrainAction LastAction { get { return _lastAction; } set { _lastAction=value.Clone();} }

        public string ID { get { return _id; } set { _id = value; } }

        public bool IsActive 
        { 
            get { return !_state.Get(PlayerState.Folded); }
            set {
                StateChanged(SetState(PlayerState.Folded, !value));
            }
        }

        public int CallRaiseCount
        {
            get { return _callCount + _raiseCount; }
        }

        public double AttackValue
        {
            get
            {
                double bet0 = Game.GetBlindAmount(this);
                //if (bet0 == 0)
                //    bet0 = Game.BigBlindAmount;

                double N = 0;

                N = (Stake - bet0) / Game.GetBet(this); 
                return N;
            }
        }
        public bool IsWinner
        {
            get { return !_state.Get(PlayerState.Winner); }
            set
            {
                StateChanged(SetState(PlayerState.Winner, value));
            }
        }

        public Odds Odds { 
            get {
                //if (double.IsNaN(_info.Odds.Value))
                  //  Game.CalcOdds(this);
                return _info.Odds; 
            } 
            set { _info.Odds=value;} 
        }

        public BitSet State { get { return _state; } }

        public IList<HoldemAction> History { get { return _hist; } }

        public bool IsShown
        {
            get { return _state.Get(PlayerState.Shown);  }
            set {
                StateChanged(SetState(PlayerState.Shown, value));
            }
        }

        public bool ForceShow
        {
            get { return _state.Get(PlayerState.ForceShow); }
            set { StateChanged(SetState(PlayerState.ForceShow, value)); }
        }

        public uint HandValue { get { return _handValue; } set { _handValue=value;} }

        public int RaiseCount { get { return _raiseCount; } set { _raiseCount=value;} }

        public int CallCount { get { return _callCount; } set { _callCount=value;} }


        public void OnAction(GamePlayer p, BrainAction ba)
        {
            switch (ba.ID)
            {
                case BrainAction.CALL: 
                    if(p==this)
                        _callCount++; 
                    break;
                case BrainAction.RAISE: 
                    if(p==this)
                        _raiseCount++; 
                    break;
                case BrainAction.FOLD: 
                    if(p==this)
                        IsActive = false; 
                    break;
                case BrainAction.BLIND:
                    break;
                default: throw new InvalidOperationException();
            }
            if(p==this)
                _lastAction = ba;
        }

        public void OnStageChanged()
        {
            this.Odds.Reset();
        }

        public void OnPocketCards(GamePlayer p)
        {
            this.Odds.Reset();
            this.BrainAction = null;
        }

        public void OnBoardCards()
        {
            this.Odds.Reset();
        }

        protected BitSet SetState(int state, bool on)
        {
            BitSet old = _state.Clone();
            _state.Set(state, on);
            return old;
        }

        public int Position { get { return Game.Players.PositionOf(this); } }

        public int Index { get { return Game.Players.IndexOf(this); } }

        public double Stake { get { return _stake; } set { _stake = value; } }

        public void NewGame()
        {
            ResetState();
            NewRound();
        }

        public void NewRound()
        {
            //_state.Set(PlayerState.Folded, false);
            Stake = 0;
            RaiseCount = 0;
            CallCount = 0;
            _brainAction = null;
            _handValue = 0;
            _lastAction = null;
            Odds.Reset();
        }

        public bool IsCurrent
        {
            get { return this.Game.Players.Current==this;}
        }

        private void ResetState()
        {
            if (IsActive)
                MyLogger.WriteLine("UNFLODING " + this);
            IsActive = true;
            IsShown = false;
            IsWinner = false;
            Odds.Reset();
        }

        public double Bank { get { return _bank; } set { _bank = value;} }

        public CardSet PocketCards { get { return _pocketCards; } set { _pocketCards = value; } }

        public int VisiblePocketCardsCount { get { return IsVisible ? PocketCards.Count : 0; } }

        public bool IsVisible { get { return  IsShown || ForceShow || IsManual; } }

        public bool IsManual { get { return State.Get(PlayerState.ManualMode); } set { State.Set(PlayerState.ManualMode, value); Game.StateChanged(); } }

        public bool IsActionReady { get { return BrainAction != null; } }

        protected override void GameChanged(Game old)
        {
            _hist = Game.History.FilterBy(
                delegate(HoldemAction ha)
                {
                    PlayerAction pa = ha as PlayerAction;
                    return null!=pa && this.ID.Equals(pa.PlayerID)
                        ||
                        ha is BoardCardsAction;
                }
                );
        }

        protected virtual void StateChanged(BitSet old)
        {

        }

        [Editor(typeof(Layers.UI.SelectEditor), typeof(System.Drawing.Design.UITypeEditor))]
        [Layers.UI.SelectFrom("Brains")]
        public IBrains Brains { get { return _brains; } set { _brains=value;} }

        public BrainAction Think()
        {
            this._brainAction = _brains.Think(Game, this);
            return this._brainAction;
        }

 

        public string StateString()
        {
            StringBuilder sb = new StringBuilder();

            if (_state.Get(PlayerState.Folded))
                sb.Append("F|");
            else
                sb.Append(" |");
            if (_state.Get(PlayerState.Shown))
                sb.Append("S|");
            else
                sb.Append(" |");
            if (_state.Get(PlayerState.Winner))
                sb.Append("W|");
            else
                sb.Append(" |");

            sb.AppendFormat("R{0}", RaiseCount);
            sb.AppendFormat("C{0}", CallCount);

            sb.AppendFormat("|A{0:F0}", AttackValue);

            {
                sb.AppendFormat("|P:");
                if (double.IsNaN(Odds.Value))
                    sb.Append("n/a");
                else
                    sb.AppendFormat("{0:F2}%", Odds.Value * 100);
            }

            return sb.ToString();
        }

        public string HandString
        {
            get
            {
                if (_handValue != 0)
                    return string.Format("{0}\n", Hand.DescriptionFromHandValue(_handValue));
                return "";
            }
        }

        public override string ToString()
        {
            return string.Format("{0}:{1}",ID,Brains!=null?Brains.ID:"null");
        }


        #region IContainer Members

        void IContainer.Add(IComponent component, string name)
        {
            throw new NotImplementedException();
        }

        void IContainer.Add(IComponent component)
        {
            throw new NotImplementedException();
        }

        ComponentCollection IContainer.Components
        {
            get { throw new NotImplementedException(); }
        }

        void IContainer.Remove(IComponent component)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}